/* serialize.h
 * All the serialization stuff
 * 
 * Copyright 2013 Akash Rawal
 * This file is part of MTC.
 * 
 * MTC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MTC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MTC.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * \addtogroup mtc_serialize
 * \{
 * 
 * This section documents functions and macros useful for serialization.
 * It is used internally by MTC. 
 * 
 * The data is serialized to 'dual stream', one is a stream of bytes, 
 * the other is the stream of memory blocks.
 * 
 * You probably don't need to learn more than this if you are not 
 * writing your own serializers or similarly not dealing with its 
 * internals.
 * 
 * In MTC reading is done in two stages. First, segments are read off
 * the 'dual stream'. Here assertions for the stream lengths are 
 * performed as a protection against reading/writing past the end of
 * stream. Then the individual segments are read without further
 * assertions. This is to minimize the number of assertions 
 * (and hence the number of 'failure points') in the code.
 */

/**A function prototype used in MtcMBlock
 * \param data One paramater to pass to the function
 */
typedef void (*MtcMFunc) (void *data);

///A structure representing a memory block.
typedef struct
{
	///Memory block.
	void *data;
	///Size of the memory block.
	uint32_t len;
	///Function used to free _data_, called when the message is destroyed.
	MtcMFunc free_func;
	///Argument to pass to _free_func_.
	void *free_func_data;
} MtcMBlock;

///A structure that you can take help of to count how large the 
///serialized data will be.
///Autogenerated serializers also use this internally.
typedef struct 
{
	///No. of bytes in 'byte stream'
	size_t n_bytes;
	///No. of blocks in 'block stream'
	size_t n_blocks;
} MtcDLen;

/**Initializes the counter
 * \param self A pointer to structure of type MtcDLen
 */
#define mtc_dlen_zero(self) \
	(self)->n_bytes = (self)->n_blocks = 0

/**A structure to iterate over 'dual stream'. This can be used in
 * [de]serialization. 
 * Autogenerated serializers also use this internally.
 */
typedef struct
{
	///Current position in byte stream
	char *bytes;
	///A pointer that points just after the last byte in byte stream
	char *bytes_lim;
	///Current position in block stream
	MtcMBlock *blocks;
	///A pointer that points just after the last block in block stream
	MtcMBlock *blocks_lim;
} MtcDStream;

/**A segment of 'dual stream'
 */
typedef struct
{
	///Current position in byte stream
	char *bytes;
	///Current position in block stream
	MtcMBlock *blocks;
} MtcSegment;

/**Gets a segment from a 'dual stream', advancing its position forward. 
 * \param self The 'dual stream'
 * \param n_bytes The number of bytes to 'read' from the byte stream
 * \param n_blocks The number of blocks to 'read' from the block stream
 * \param res Pointer to the resulting segment struct
 * \return 0 if the operation was successful, -1 otherwise
 */
int mtc_dstream_get_segment
	(MtcDStream *self, size_t n_bytes, size_t n_blocks, 
	 MtcSegment *res);

/**Determines whether the 'dual stream' is empty.*/
#define mtc_dstream_is_empty(self) \
	(((self)->bytes_lim - (self)->bytes) \
	 + ((self)->blocks_lim - (self)->blocks) ? 0 : 1)

//writing unsigned integers
/**Stores a 1-byte unsigned char to the current segment position
 * and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Value to store (type: unsigned char)
 */
#define mtc_segment_write_uchar(seg, val) \
do { \
	*((seg)->bytes) = val; \
	(seg)->bytes += 1; \
} while (0)
		
/**Assigns a 16-bit unsigned integer to current segment position
 * after converting to little endian and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint16_t holding the value to assign
 *        (Must be a variable. To write a constant assign it to a variable
 *        first)
 */
#define mtc_segment_write_uint16(seg, lval) \
do { \
	char *le_ptr, *h_ptr; \
	le_ptr = (seg)->bytes; \
	h_ptr = (char *) &lval; \
	mtc_uint16_copy_to_le(le_ptr, h_ptr); \
	(seg)->bytes += 2; \
} while (0)

/**Assigns a 32-bit unsigned integer to current segment position
 * after converting to little endian and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint32_t holding the value to assign
 *        (Must be a variable. To write a constant assign it to a variable
 *        first)
 */
#define mtc_segment_write_uint32(seg, lval) \
do {\
	char *le_ptr, *h_ptr; \
	le_ptr = (seg)->bytes; \
	h_ptr = (char *) &lval; \
	mtc_uint32_copy_to_le(le_ptr, h_ptr); \
	(seg)->bytes += 4; \
} while(0)

/**Assigns a 64-bit unsigned integer to current segment position
 * after converting to little endian and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint64_t holding the value to assign
 *        (Must be a variable. To write a constant assign it to a variable
 *        first)
 */
#define mtc_segment_write_uint64(seg, lval) \
do {\
	char *le_ptr, *h_ptr; \
	le_ptr = (seg)->bytes; \
	h_ptr = (char *) &lval; \
	mtc_uint64_copy_to_le(le_ptr, h_ptr); \
	(seg)->bytes += 8; \
} while(0)

//reading unsigned integers
/**Retrives a 1-byte unsigned char to the current segment position
 * and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type unsigned char to store the result
 */
#define mtc_segment_read_uchar(seg, lval) \
do { \
	lval = *((seg)->bytes); \
	(seg)->bytes += 1; \
} while (0)

/**Retrives a 16-bit unsigned integer from current segment position
 * after converting to host byte order and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint16_t to store the result
 */
#define mtc_segment_read_uint16(seg, lval) \
do {\
	char *le_ptr, *h_ptr; \
	le_ptr = (seg)->bytes; \
	h_ptr = (char *) &lval; \
	mtc_uint16_copy_from_le(le_ptr, h_ptr); \
	(seg)->bytes += 2; \
} while(0)

/**Retrives a 32-bit unsigned integer from current segment position
 * after converting to host byte order and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint32_t to store the result
 */
#define mtc_segment_read_uint32(seg, lval) \
do {\
	char *le_ptr, *h_ptr; \
	le_ptr = (seg)->bytes; \
	h_ptr = (char *) &lval; \
	mtc_uint32_copy_from_le(le_ptr, h_ptr); \
	(seg)->bytes += 4; \
} while(0)

/**Retrives a 64-bit unsigned integer from current segment position
 * after converting to host byte order and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint64_t to store the result
 */
#define mtc_segment_read_uint64(seg, lval) \
do {\
	char *le_ptr, *h_ptr; \
	le_ptr = (seg)->bytes; \
	h_ptr = (char *) &lval; \
	mtc_uint64_copy_from_le(le_ptr, h_ptr); \
	(seg)->bytes += 8; \
} while(0)

#ifdef MTC_INT_2_COMPLEMENT
#define mtc_segment_write_char mtc_segment_write_uchar
#define mtc_segment_write_int16 mtc_segment_write_uint16
#define mtc_segment_write_int32 mtc_segment_write_uint32
#define mtc_segment_write_int64 mtc_segment_write_uint64
#define mtc_segment_read_char mtc_segment_read_uchar
#define mtc_segment_read_int16 mtc_segment_read_uint16
#define mtc_segment_read_int32 mtc_segment_read_uint32
#define mtc_segment_read_int64 mtc_segment_read_uint64
#else

//writing signed integers
/**Assigns a signed char to current segment position
 * and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Value to store (type char)
 */
#define mtc_segment_write_char(seg, lval) \
	mtc_segment_write_uchar(seg, mtc_char_to_2_complement(lval))

/**Assigns a 16-bit signed integer to current segment position
 * after converting to little endian and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint16_t holding the value to assign
 *        (Must be a variable. To write a constant assign it to a variable
 *        first)
 */
#define mtc_segment_write_int16(seg, lval)
do { \
	uint16_t lval_2c = mtc_int16_to_2_complement(lval); \
	mtc_segment_write_uint16(seg, lval_2c); \
} while(0)

/**Assigns a 32-bit signed integer to current segment position
 * after converting to little endian and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint32_t holding the value to assign
 *        (Must be a variable. To write a constant assign it to a variable
 *        first)
 */
#define mtc_segment_write_int32(seg, lval)
do { \
	uint32_t lval_2c = mtc_int32_to_2_complement(lval); \
	mtc_segment_write_uint32(seg, lval_2c); \
} while(0)

/**Assigns a 64-bit signed integer to current segment position
 * after converting to little endian and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint64_t holding the value to assign
 *        (Must be a variable. To write a constant assign it to a variable
 *        first)
 */
#define mtc_segment_write_int64(seg, lval)
do { \
	uint64_t lval_2c = mtc_int64_to_2_complement(lval); \
	mtc_segment_write_uint64(seg, lval_2c); \
} while(0)

//reading signed integers
/**Retrives a signed character from current segment position
 * after converting to host byte order and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type char to store the result
 */
#define mtc_segment_read_char(seg, lval)
do { \
	mtc_segment_read_uchar(seg, lval); \
	lval = mtc_char_to_2_complement(lval); \
} while(0)

/**Retrives a 16-bit signed integer from current segment position
 * after converting to host byte order and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint16_t to store the result
 */
#define mtc_segment_read_int16(seg, lval)
do { \
	mtc_segment_read_uint16(seg, lval); \
	lval = mtc_int16_to_2_complement(lval); \
} while(0)

/**Retrives a 32-bit signed integer from current segment position
 * after converting to host byte order and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint32_t to store the result
 */
#define mtc_segment_read_int32(seg, lval)
do { \
	mtc_segment_read_uint32(seg, lval); \
	lval = mtc_int32_to_2_complement(lval); \
} while(0)

/**Retrives a 64-bit signed integer from current segment position
 * after converting to host byte order and increments it accordingly.
 * \param seg Pointer to the segment (type MtcSegment *)
 * \param lval Variable of type uint64_t to store the result
 */
#define mtc_segment_read_int64(seg, lval)
do { \
	mtc_segment_read_uint64(seg, lval); \
	lval = mtc_int64_to_2_complement(lval); \
} while(0)

#endif

/**Structure that you can use to portably store any floating point value
 * that IEEE 754 supports.
 * MDL type flt32 and flt64 map to this type.
 */
typedef struct
{
	///Type of floating point value stored
	MtcFltType type;
	/**The floating point value stored. It should store correct value 
	 * when _type_ is MTC_FLT_ZERO or MTC_FLT_NORMAL, otherwise
	 * it may store any value.
	 */
	double val;
} MtcValFlt;

/**Stores the given floating point value at current segment position 
 * in IEEE 754 32-bit format and increments the position accordingly.
 * \param seg Pointer to the segment
 * \param val The value to store
 */
void mtc_segment_write_flt32(MtcSegment *seg, MtcValFlt val);

/**Retrives a floating point value from current segment position
 * in IEEE 754 32-bit format and increments the position accordingly. 
 * \param seg Pointer to the segment
 * \param val  Pointer indicating where to store the value
 */
void mtc_segment_read_flt32(MtcSegment *seg, MtcValFlt *val);

/**Stores the given floating point value at current segment position 
 * in IEEE 754 64-bit format and increments the position accordingly.
 * \param seg Pointer to the segment
 * \param val The value to store
 */
void mtc_segment_write_flt64(MtcSegment *seg, MtcValFlt val);

/**Retrives a floating point value from current segment position
 * in IEEE 754 64-bit format and increments the position accordingly. 
 * \param seg Pointer to the segment
 * \param val  Pointer indicating where to store the value
 */
void mtc_segment_read_flt64(MtcSegment *seg, MtcValFlt *val);

/**Adds a null-terminated string to the current segment position 
 * and increments the segment accordingly.
 * \param seg Pointer to the segment.
 * \param val The null-terminated string to add
 */
void mtc_segment_write_string(MtcSegment *seg, char *val);

/**Retrieves a null-terminated string from the current segment position 
 * and increments the segment accordingly.
 * \param seg Pointer to the segment.
 * \return The string just read off or NULL if operation failed. 
 *         Use free() to free it. 
 */
char *mtc_segment_read_string(MtcSegment *seg);

/**A structure that can be used for managing memory blocks shared between 
 * messages and source data.
 */
typedef struct
{
	///Function used to add a reference to the concerned memory block, or NULL
	MtcMFunc ref_func;
	///Function used to remove a reference from the concerned memory block,
	///or NULL
	MtcMFunc unref_func;
} MtcMRef;

///An MtcMRef value that you can use if you do not need to manage the memory
///(e.g. in case of statically/automatically allocated arrays)
extern const MtcMRef mtc_m_ref_dumb;

///A structure, that MDL type _raw_ maps to.
typedef struct 
{
	///The memory block.
	void *mem;
	///Size of the memory block.
	size_t size;
	///Pointer to structure containing memory management functions,
	///or NULL to indicate that memory should be copied, not shared.
	const MtcMRef *ref;
	///Data to pass to the memory management functions
	void *data;
} MtcValRaw;

#define mtc_val_raw_null(val) \
do { \
	(val)->mem = NULL; \
	(val)->size = 0; \
	(val)->ref = NULL; \
	(val)->data = NULL; \
} while (0)

/**Stores a _raw_ type in the current segment position and increments 
 * the segment's positions accordingly. 
 * \param seg Pointer to the segment
 * \param val The value to store
 */
void mtc_segment_write_raw(MtcSegment *seg, MtcValRaw val);

/**Retrives a _raw_ type from the current segment position and 
 * increments the segment's positions accordingly. 
 * \param seg Pointer to the segment
 * \param val The value to store
 */
void mtc_segment_read_raw(MtcSegment *seg, MtcValRaw *val);


//Common header for all types of data used on MtcFDLink
//No padding to be assumed
typedef struct
{
	//For error detection and protocol versioning.
	char m, t, c, zero;
	
	//No. of blocks inside a message.
	//For signal it is 0.
	uint32_t size;
	
	//Destination to send data to
	uint64_t dest;
	
	//Destination to send reply (or errors if any) to
	uint64_t reply_to;
	
	//For message this is followed by 'block size index' (BSI)
	//indicating size of each extra block followed by the actual data.
	//For signal it is only 1 element long containing signal
	//The size 2 here has no semantic meaning and is not to be 
	//respected.
	uint32_t data[2];
} MtcHeader;

//Structure containing only useful data elements from MtcHeader
typedef struct 
{
	uint64_t dest, reply_to;
	uint32_t size, data_1;
} MtcHeaderData;

//Type for the buffer for message
//uint64_t is just to force alignment with hope of increasing 
//performance
typedef struct {uint64_t data[4]; } MtcHeaderBuf;

//Macro to calculate size of the header
#define mtc_header_sizeof(size) (24 + (4 * (size)))

//Macro to calculate minimum size of the header
//It is the size of data that is read first by MtcFDLink which tells
//about size of the rest of the header
#define mtc_header_min_size (28)

//Serialize the header for a message
void mtc_header_write_for_msg
	(MtcHeaderBuf *buf, uint64_t dest, uint64_t reply_to, 
	MtcMBlock *blocks, uint32_t n_blocks);

//Serialize the header for a signal
void mtc_header_write_for_signal
	(MtcHeaderBuf *buf, uint64_t dest, uint64_t reply_to, 
	uint32_t signum);

//Deserialize the message header
//returns zero for format errors
int mtc_header_read(MtcHeaderBuf *buf, MtcHeaderData *res);

///}
